import Layout from 'layout'
import React, {Component} from 'react'
import Router from 'next/router'
import classNames from 'classnames'
import Header from 'components/Header'
import {Pagination, Tag} from 'antd'
import request from 'utils/request'
import {observable, computed} from 'mobx'
import {observer, Provider, inject} from 'mobx-react'
import Empty from 'components/Empty'
import navigationStore from 'store/navigation'
import { navigation, passage, title, hash, markdown, tooltipStyle } from 'styles/homepage'
import throttle from 'utils/throttle'

const Tooltip = inject('store')(
  observer(({store}) => {
    const tooltip = store.tooltip
    return tooltip.show &&
        <div id="tooltip" className="ant-tooltip ant-tooltip-placement-top"
          style={{top:tooltip.pos.y-30, left: tooltip.pos.x}}>
          <div className="ant-tooltip-content">
            <div className="ant-tooltip-arrow"></div>
            <div className="ant-tooltip-inner">prompt text</div>
          </div>
          <style jsx>{tooltipStyle}</style>        
        </div>
  })
)

const NavigationPagination = inject('store', 'onPageChange')(
  observer(({store, onPageChange}) => {
    return (
      <div>
        <Pagination
          simple
          total={store.count}
          pageSize={store.pageSize}
          current={Number(store.url.query.page)}
          onChange={onPageChange}
        />
      </div>
    )
  })
)

const NavigationTitle = inject('store', 'onClickTitle')(
  observer(({store, onClickTitle, index, title, id, href}) => {
    return (
      <div
        className="navigation-title"
        style={{color: store.currentId === id ? '#e25e5e' : '#444'}}
        onClick={() => onClickTitle({id, href})}
      >
        {store.pageSize*(store.url.query.page-1) + index+1}. {title}
        <style jsx>{`
          .navigation-title{
            font-weight: 400;
            cursor: pointer;
            margin-bottom: 5px;
          }
        `}</style>
      </div>
    )
  })
)

const Navigation = inject('store','navigationStore')(
  observer(({store, navigationStore}) => {
  return (
    <div className={`navigation ${navigationStore.open ? '' : 'navi-close'}`}>
      <div className="navigation-area">
        <h3>{store.listType}</h3>
        {store.list.map((li, i) => 
          <NavigationTitle
            title={li.title}
            href={li.href}
            id={li._id}
            index={i}
            key={i}
          />)}
        <NavigationPagination />
      </div>
      <style jsx>{navigation}</style>
    </div>
  )
}))

const PassageTitle = inject('store')(observer(({store}) => {
  return (
    <div id="main_title_container">
      <p id="main_title">{store.detail.title}</p>
      <p>2018-03-14</p>
      <style jsx>{title}</style>
    </div>
  )
}))

const HashContainer = inject('store', 'onClickHash')(observer(({store, onClickHash}) => {
  return (
    <div className="hash-container">
        <a className="hash1 title" href="#main_title">{store.detail.title}</a>
        {store.hash.map(hash => (
            <a
              className={classNames(hash.level === 1?'hash1' : 'hash2', {active: store.activeHash.id === hash.id})}
              key={hash.id}
              href={`${location.pathname}${location.search}#${hash.id}`}
              onClick={() => onClickHash(hash)}
            >{hash.title}</a>
        ))}
      <style jsx global>{hash}</style>    
    </div>
  )
}))

 @inject('store', 'abstractHash', 'textSelection') @observer
class Passage extends Component {
  componentDidUpdate(){
  }
  render(){
    const store = this.props.store
    const textSelection = this.props.textSelection
    return (<div>
        <div className="passage">
          <div className={`passage-area ${navigationStore.open ? '' : 'navi-close'}`}
          onMouseUp={textSelection}
          onMouseDown={()=>{store.tooltip.show = false}}>
            <PassageTitle />
            <div dangerouslySetInnerHTML={{__html: store.detail.htmlContent}}></div>
            <div>
            {store.detail.tag.map((t, i) => 
              t ? <Tag key={t.name}>{t.name}</Tag> : null
            )}
            </div>
          </div>
        </div>
        {store.hash.length && <HashContainer />}
        <style jsx>{passage}</style>
        <style jsx global>{markdown}</style>
      </div>)
  }
}


const Content = inject('store')(observer(({store}) => {
  return (
    <div className="content">
      <Navigation /> 
      {store.error.blog ? 
        (<div className="passage">
          <Empty text={store.error.blog} />
        </div>):
        <Passage />
      }
      <style jsx>{passage}</style>    
    </div>
  )
}))

class App extends Component{

  static async getInitialProps({query, req, pathname, asPath}){
    const isMobile = req ? 
    /Android|webOS|iPhone|iPad|iPod|Opera Mini/i.test(req.headers['user-agent'])
    : window.innerWidth <= 500

    query.page = query.page || 1
    const url = {
      pathname, asPath, query
    }

    console.log('getInitialProps with:')
    console.log(`tag:${query.tag}, blog:${query.blog}, page:${query.page}`)
    console.log(`pathname:${pathname}, asPath:${asPath}`)

    let list, tagName, count
    const tagId = query.tag
    if(tagId) {
      const res = (await request('get', `tag/${query.tag}?page=${query.page}`)).data
      tagName = res.tag.name
      list = res.tag.blog
      count = res.blogCount
    } else {
      const res = (await request('get', `blog/listOfDisplay?page=${query.page}`)).data
      list = res.list
      count = res.count
    }
    const currentId = query.blog || (list.length ? list[0]._id : '')
    const {data:detail, success: blogSuccess} = await request('get', `blog/${currentId}`)
    const blogError = blogSuccess ? '' : (list.length ? '查无此文！' : '暂无存货!')
    
    return {list, detail, currentId, tagId, tagName, count, blogError, isMobile, url}
  }

  constructor(props){
    super(props)
    this.store = new Store({
      list:props.list,
      listType: props.tagName ? props.tagName : 'All',
      detail:props.detail,
      count: props.count,
      currentId: props.currentId,
      tagId: props.tagId,
      error: {blog: props.blogError},
      url: props.url,
    })
    if(props.isMobile) navigationStore.open = false
  }

  componentDidMount() {
    document.title='首页'
    this.afterGetContent(this.store.currentId)
  }

  async getList() {
    let list
    const tagId = this.store.url.query.tag
    const page = this.store.url.query.page
    if(tagId) list = (await request('get', `tag/${tagId}?page=${page}`)).data.tag.blog
    else list = (await request('get', `blog/listOfDisplay?page=${page}`)).data.list
    const detail = (await request('get', `blog/${list[0]._id}`)).data
    return {list, detail}
  }

  async getContent(id) {
    this.store.detail = (await request('get', `blog/${id}`)).data
  }

  // 点击标题
  async onClickTitle({href, id}){
    this.beforeDestroyContent()
    const tagName = this.store.url.query.tagName
    const tagId = this.store.url.query.tag
    const page = this.store.url.query.page
    const actualPath = `/?${tagName?`tag=${tagId}&`:''}page=${page}&blog=${id}`
    const asPath = `${ tagName? `/tag/${tagName}` : '' }/${href}?page=${page}`
    Router.replace(actualPath, asPath, { shallow: true })
    await this.getContent(id)
    this.afterGetContent(id)
  }

  // 拉回内容后对hash等进行的处理
  afterGetContent(id) {
    this.abstractHash()
    this.store.currentId = id    
    this.locateHash()
    window.onscroll = throttle(this.locateHash.bind(this), 100) 
  }

  // 切换内容前对当页不需要信息进行销毁处理
  beforeDestroyContent() {
    window.onscroll = () => false
    this.store.error.blog = ''
    this.store.hash = []
    this.store.activeHash = {}
  }

  async onPageChange(page) {
    this.store.url.query.page = page
    const tagName = this.store.url.query.tagName
    const tagId = this.store.url.query.tag
    const actualPath = `/?${tagName?`tag=${tagId}&`:''}page=${page}`
    const asPath = `${ tagName? `/tag/${tagName}` : '' }/?page=${page}`
    Router.replace(actualPath, asPath, { shallow: true })

    const {list, detail} = await this.getList(page)
    this.store.list = list
    this.store.currentId = list[0]._id
    this.store.detail =detail
  }

  onClickHash(hash){
    // 这里也是比较丑陋的处理方式…因为不推迟时间的化，滚动事件重新定位到的activaHash会覆盖这里
    setTimeout(()=>{this.store.activeHash = hash}, 100)
  }

  // hash:{id: number, level: 1||2, top: number, title: string}
  abstractHash() {
    if(!this.store.detail) return
    const scrollY = window.scrollY    
    const passageArea = document.querySelector('.passage-area')
    const startEl = passageArea.getElementsByTagName('h2')[0] || passageArea.getElementsByTagName('h3')[0]
    if(!startEl) this.store.hash = []
    else {
      this.store.hash = defineEl(startEl)

      function defineEl(el, hashArr = []) {
        if(!el) return
        if(el.tagName === 'H2' || el.tagName === 'H3')
          hashArr.push({
            id: el.id,
            level: el.tagName === 'H2' ? 1: 2,
            top: el.getBoundingClientRect().top+120+scrollY,
            title: el.textContent
          })
        defineEl(el.nextElementSibling, hashArr)
        return hashArr
      }
    }    
  }

  locateHash() {
    const top = window.scrollY + 100
    const hash = this.store.hash
    this.store.activeHash = hash.find((h,i) => {
      return (top >= h.top && (!hash[i+1] || top < hash[i+1].top))
    }) || this.store.hash[0]
  }

  textSelection() {
    const s = window.getSelection()
    if(!s.toString()) return
    const rect = s.getRangeAt(0).getBoundingClientRect()
    this.store.tooltip = {
      show: true,
      pos: {
        x: rect.x+rect.width/2,
        y: rect.y
      }
    }
  }

  render(){
    return (
      <Provider
        store={this.store}
        navigationStore={navigationStore}
        onClickTitle={this.onClickTitle.bind(this)}
        onPageChange={this.onPageChange.bind(this)}
        abstractHash={this.abstractHash.bind(this)}
        onClickHash={this.onClickHash.bind(this)}
        textSelection={this.textSelection.bind(this)}
      >
        <Layout>
          <Header />
          <Content />
          <Tooltip />
        </Layout>
      </Provider>
    )
  }
}

class Store {
  @observable list = []
  @observable currentId
  @observable detail = {}
  @observable error = {}
  @observable hash = []
  @observable activeHash = {}
  @observable page = {}
  @observable url = {}
  @observable tooltip = {}
  constructor({list, listType, count, detail, currentId, tagId, error, url}) {
    this.list = list || []
    this.listType = listType
    this.count = count
    this.detail = detail
    this.hash = []
    this.activeHash = {}
    this.currentId = currentId
    this.tagId = tagId
    this.error = error
    this.pageSize = 2
    this.url = url
    this.tooltip = { show:false, pos:{} }
  }
}

export default App